package org.example.anno.impl;

import org.example.anno.Bootstrap;
import org.example.manager.impl.GrainManager;
import org.example.template.IBootstrap;
import org.example.util.AbstractAnnotationUtil;
import org.example.util.GenericSuperclassUtil;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;

public class BootstrapImpl {

    /**
     * 扫描被Bootstrap标记的对象，并且触发他们
     *
     * @param abstractAnnotationUtil 注解工具对象
     */
    public static void initBootstrap(AbstractAnnotationUtil<?> abstractAnnotationUtil) {
        Set<Class<?>> classes = abstractAnnotationUtil.getAnnotationByClass(Bootstrap.class);
        classes.forEach(aClass -> {
            try {
                Constructor<IBootstrap<? extends Annotation>> constructor = (Constructor<IBootstrap<? extends Annotation>>) aClass.getConstructor();
                constructor.setAccessible(true);
                IBootstrap<? extends Annotation> iBootstrap = constructor.newInstance();
                Class<? extends Annotation> argument = (Class<? extends Annotation>) GenericSuperclassUtil.getActualTypeArgument(aClass);
                if (argument == null) {
                    throw new IllegalArgumentException("未知的类的泛型 null");
                }
                Set<Class<?>> byClass = abstractAnnotationUtil.getAnnotationByClass(argument);
                iBootstrap.initClassByAnnotation(new ArrayList<>(byClass), argument);
                iBootstrap.initFiledByAnnotation(getFields(argument), argument);
                iBootstrap.initMethodByAnnotation(getMethods(argument), argument);
                iBootstrap.initParameterByAnnotation(getParameter(argument), argument);
            } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        });
    }

    private static Map<Object, List<Field>> getFields(Class<? extends Annotation> aClass) {
        GrainManager grainManager = GrainManager.getInstance();
        Map<Object, List<Field>> map = new HashMap<>();
        grainManager.forEach((ac, o) -> {
            Field[] fields = ac.getDeclaredFields();
            if (fields.length == 0) {
                return;
            }
            List<Field> list = new ArrayList<>();
            for (Field field : fields) {
                Annotation annotation = field.getDeclaredAnnotation(aClass);
                if (annotation == null) {
                    continue;
                }
                list.add(field);
            }
            map.put(o, list);
        });
        return map;
    }

    private static Map<Object, List<Method>> getMethods(Class<? extends Annotation> aClass) {
        GrainManager grainManager = GrainManager.getInstance();
        Map<Object, List<Method>> map = new HashMap<>();
        grainManager.forEach((ac, o) -> {
            Method[] methods = ac.getDeclaredMethods();
            if (methods.length == 0) {
                return;
            }
            List<Method> list = new ArrayList<>();
            for (Method method : methods) {
                Annotation annotation = method.getDeclaredAnnotation(aClass);
                if (annotation == null) {
                    continue;
                }
                list.add(method);
            }
            if (list.isEmpty()) {
                return;
            }
            map.put(o, list);
        });
        return map;
    }

    private static Map<Object, Map<Method, List<Parameter>>> getParameter(Class<? extends Annotation> aClass) {
        Map<Object, Map<Method, List<Parameter>>> map = new HashMap<>();
        GrainManager grainManager = GrainManager.getInstance();
        grainManager.forEach((ac, o) -> {
            Method[] methods = ac.getDeclaredMethods();
            if (methods.length == 0) {
                return;
            }
            for (Method method : methods) {
                Parameter[] parameters = method.getParameters();
                if (parameters.length == 0) {
                    continue;
                }
                Map<Method, List<Parameter>> listMap = map.computeIfAbsent(o, k -> new HashMap<>());
                for (Parameter parameter : parameters) {
                    Annotation annotation = parameter.getDeclaredAnnotation(aClass);
                    if (annotation == null) {
                        continue;
                    }
                    List<Parameter> list = listMap.computeIfAbsent(method, k -> new ArrayList<>());
                    list.add(parameter);
                }
            }
        });
        return map;
    }


}
